package cn.fboost.mtos.service.impl;

import cn.fboost.mtos.entity.Order;
import cn.fboost.mtos.mapper.OrderMapper;
import cn.fboost.mtos.service.CallbackService;
import cn.fboost.mtos.util.WeChatUtil;
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import com.wechat.pay.java.service.refund.model.Status;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;

@Slf4j
@Service
public class CallbackServiceImpl implements CallbackService {
    private final WeChatUtil weChatUtil;
    private final OrderMapper orderMapper;

    public CallbackServiceImpl(WeChatUtil weChatUtil, OrderMapper orderMapper) {
        this.weChatUtil = weChatUtil;
        this.orderMapper = orderMapper;
    }

    @Override
    public String wechatPayCallback(String body, HttpServletRequest request, HttpServletResponse response) {
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(request.getHeader("Wechatpay-Serial"))
                .nonce(request.getHeader("Wechatpay-Nonce"))
                .signature(request.getHeader("Wechatpay-Signature"))
                .signType(request.getHeader("Wechatpay-Signature-Type"))
                .timestamp(request.getHeader("Wechatpay-Timestamp"))
                .body(body)
                .build();

        Config payConfig = weChatUtil.getPayConfig();
        NotificationParser parser = new NotificationParser((NotificationConfig) payConfig);

        // 通知类型判断
        JSONObject jsonObject = JSONObject.parseObject(body);
        String eventType = jsonObject.getString("event_type");
        if (eventType == null) {
            response.setStatus(500);
            return JSONObject.toJSONString(new HashMap<>(){{
                put("code", "FAIL");
                put("message", "event type is null");
            }});
        } else if (eventType.startsWith("TRANSACTION")) {
            // 支付通知
            Transaction decryptObject = parser.parse(requestParam, Transaction.class);

            // 获取订单号
            String tradeNo = decryptObject.getOutTradeNo();
            Order order = orderMapper.findByTradeNo(tradeNo);
            if (order == null) {
                log.error("微信支付回调遇到未知订单号，回调详情: {}", decryptObject);

                response.setStatus(500);
                return JSONObject.toJSONString(new HashMap<>(){{
                    put("code", "FAIL");
                    put("message", "order not found");
                }});
            }

            if (order.getStatus() == Order.Status.UNPAID) {
                // 订单状态未支付事，更新订单状态
                Order update = new Order();
                update.setId(order.getId());
                update.setStatus(Order.Status.PAID);
                update.setOutTradeNo(decryptObject.getTransactionId());
                update.setUpdateTime(new Date());
                if (orderMapper.update(update) == 0) {
                    response.setStatus(500);
                    return JSONObject.toJSONString(new HashMap<>(){{
                        put("code", "FAIL");
                        put("message", "order update fail");
                    }});
                }
            }

            response.setStatus(200);
            return null;
        } else if (eventType.startsWith("REFUND")){
            // 退款结果通知
            RefundNotification decryptObject = parser.parse(requestParam, RefundNotification.class);
            Order order = orderMapper.findByTradeNo(decryptObject.getOutTradeNo());
            if (order == null) {
                log.error("微信退款回调遇到未知订单号，回调详情: {}", decryptObject);

                response.setStatus(500);
                return JSONObject.toJSONString(new HashMap<>(){{
                    put("code", "FAIL");
                    put("message", "order not found");
                }});
            }

            if (order.getStatus() == Order.Status.REFUNDED) {
                // 订单状态已退款，直接返回
                response.setStatus(200);
                return null;
            }

            Status refundStatus = decryptObject.getRefundStatus();
            if (refundStatus == Status.SUCCESS) {
                // 退款成功
                Order update = new Order();
                update.setId(order.getId());
                update.setStatus(Order.Status.REFUNDED);
                update.setUpdateTime(new Date());
                if (orderMapper.update(update) == 0) {
                    response.setStatus(500);
                    return JSONObject.toJSONString(new HashMap<>(){{
                        put("code", "FAIL");
                        put("message", "order update fail");
                    }});
                }
            } else if (refundStatus == Status.ABNORMAL || refundStatus == Status.CLOSED) {
                // 退款异常/退款关闭
                Order update = new Order();
                update.setId(order.getId());
                update.setStatus(Order.Status.REFUND_FAILED);
                update.setUpdateTime(new Date());
                if (orderMapper.update(update) == 0) {
                    response.setStatus(500);
                    return JSONObject.toJSONString(new HashMap<>(){{
                        put("code", "FAIL");
                        put("message", "order update fail");
                    }});
                }
            }

            // 其他未知的退款状态
            response.setStatus(500);
            return JSONObject.toJSONString(new HashMap<>(){{
                put("code", "FAIL");
                put("message", "unknown refund status");
            }});
        }

        // 其他类型的通知不处理
        response.setStatus(500);
        return JSONObject.toJSONString(new HashMap<>(){{
            put("code", "FAIL");
            put("message", "unknown event type");
        }});
    }
}
